#ifndef OPTFRAME_NSSEQVRPEXCHANGE_HPP_
#define OPTFRAME_NSSEQVRPEXCHANGE_HPP_

// Framework includes
#include "../OptFrame/Move.hpp"
#include "../OptFrame/NSSeq.hpp"

using namespace std;

// Working structure: vector<T>

class MoveVRPExchange: public Move<RepPRV, MemPRVP>
{
protected:
	int c1, c2; // client 1 and client 2, respectively
	int r; //route

	PRVPProblemInstance& pPRVP;

public:

	MoveVRPExchange(PRVPProblemInstance& _pPRVP, int _r, int _c1, int _c2) :
		pPRVP(_pPRVP), r(_r), c1(_c1), c2(_c2)
	{
	}

	virtual ~MoveVRPExchange()
	{
	}

	int get_c1()
	{
		return c1;
	}

	int get_c2()
	{
		return c2;
	}

	int get_r()
	{
		return r;
	}

	bool canBeApplied(const RepPRV& rep)
	{
		bool all_positive = (c1 >= 0) && (c2 >= 0) && (r >= 0);
		return all_positive && (rep.at(r).size() >= 2);
	}

	MoveVRPExchange& apply(RepPRV& rep)
	{

		int aux = rep.at(r).at(c1);
		rep.at(r).at(c1) = rep.at(r).at(c2);
		rep.at(r).at(c2) = aux;

		return *new MoveVRPExchange(pPRVP, r, c1, c2);
	}

	MoveVRPExchange& apply(MemPRVP& m, RepPRV& rep)
	{

		Matrix<double>& dist = (*pPRVP.dist);
		double delta = 0;

		//para facilitar as contas, adicionando o depósito no final da rota
		rep.at(r).push_back(0);

		if (c1 - 1 < 0)
			delta += -dist(0, rep.at(r).at(c1)) - dist(rep.at(r).at(c1), rep.at(r).at(c1 + 1));
		else
			delta += -dist(rep.at(r).at(c1 - 1), rep.at(r).at(c1)) - dist(rep.at(r).at(c1), rep.at(r).at(c1 + 1));

		if (c2 - 1 < 0)
			delta += -dist(0, rep.at(r).at(c2)) - dist(rep.at(r).at(c2), rep.at(r).at(c2 + 1));
		else
			delta += -dist(rep.at(r).at(c2 - 1), rep.at(r).at(c2)) - dist(rep.at(r).at(c2), rep.at(r).at(c2 + 1));

		if (c1 - 1 < 0)
			delta += dist(0, rep.at(r).at(c2)) + dist(rep.at(r).at(c2), rep.at(r).at(c1 + 1));
		else
			delta += dist(rep.at(r).at(c1 - 1), rep.at(r).at(c2)) + dist(rep.at(r).at(c2), rep.at(r).at(c1 + 1));

		if (c2 - 1 < 0)
			delta += dist(0, rep.at(r).at(c1)) + dist(rep.at(r).at(c1), rep.at(r).at(c2 + 1));
		else
			delta += dist(rep.at(r).at(c2 - 1), rep.at(r).at(c1)) + dist(rep.at(r).at(c1), rep.at(r).at(c2 + 1));

		if ((c1 + 1 == c2) || (c2 + 1 == c1))
			delta += 2*dist(rep.at(r).at(c1), rep.at(r).at(c2));

		//removendo os depósitos que facilitaram a conta
		rep.at(r).pop_back();
		//setando as variações de fo e inviabilidade
		m.setDelta(delta);
		m.setDelta_inf(0.0);

		//executando o movimento
		int aux = rep.at(r).at(c1);
		rep.at(r).at(c1) = rep.at(r).at(c2);
		rep.at(r).at(c2) = aux;

		return *new MoveVRPExchange(pPRVP, r, c1, c2);
	}

	virtual bool operator==(const Move<RepPRV, MemPRVP>& _m) const
	{
		const MoveVRPExchange& m1 = (const MoveVRPExchange&) _m;
		return ((m1.c1 == c1) && (m1.c2 == c2) && (m1.r == r));
	}

	void print() const
	{
		cout << "MoveVRPExchange( route: " << r << " : ";
		cout << " client " << c1 << " <=>  client" << c2 << " )";
		cout << endl;
	}
};

class NSIteratorVRPExchange: public NSIterator<RepPRV, MemPRVP>
{

private:

	MoveVRPExchange* m;
	int index;
	vector<MoveVRPExchange*> moves;
	const RepPRV& rep;

	PRVPProblemInstance& pPRVP;

public:

	NSIteratorVRPExchange(PRVPProblemInstance& _pPRVP, const RepPRV& _r) :
		pPRVP(_pPRVP), rep(_r)
	{
		index = 0;
		m = NULL;
	}

	virtual ~NSIteratorVRPExchange()
	{
	}

	void first()
	{
		for (int r = 0; r < rep.size(); r++)
		{
			for (int c1 = 0; c1 < rep.at(r).size(); c1++)
			{
				for (int c2 = 0; c2 < rep.at(r).size(); c2++)
				{
					if (c1 != c2)
						moves.push_back(new MoveVRPExchange(pPRVP, r, c1, c2));
				}
			}
		}
		if (moves.size() > 0)
		{
			m = moves[index];
		}
		else
			m = NULL;
	}

	void next()
	{
		index++;
		if (index < moves.size())
		{
			m = moves[index];
		}
		else
			m = NULL;
	}

	bool isDone()
	{
		return m == NULL;
	}

	MoveVRPExchange& current()
	{
		if (isDone())
		{
			cout << "There isnt any current element!" << endl;
			cout << "NSSeqVRPExchange. Aborting." << endl;
			exit(1);
		}

		return *m;
	}
};

class NSSeqVRPExchange: public NSSeq<RepPRV, MemPRVP>
{

private:
	PRVPProblemInstance& pPRVP;

public:

	NSSeqVRPExchange(PRVPProblemInstance& _pPRVP) :
		pPRVP(_pPRVP)
	{
	}

	virtual ~NSSeqVRPExchange()
	{
	}

	MoveVRPExchange& move(const RepPRV& rep)
	{
		int r = rand() % rep.size();
		if (rep.at(r).size() < 2)
			return *new MoveVRPExchange(pPRVP, -1, -1, -1);

		int c1 = rand() % rep.at(r).size();

		int c2;

		do
		{
			c2 = rand() % rep.at(r).size();
		} while (c1 == c2);

		// create exchange(p1,p2) move
		return *new MoveVRPExchange(pPRVP, r, c1, c2);
	}

	virtual NSIteratorVRPExchange& getIterator(const RepPRV& r)
	{
		return *new NSIteratorVRPExchange(pPRVP, r);
	}

	virtual void print()
	{
		cout << "NSSeqVRPExchange" << endl;
	}
};

#endif /*OPTFRAME_NSSEQVRPEXCHANGE_HPP_*/
